#!/usr/local/bin/python3
import threading
import time

class State:
    def __init__(self):
        self.error()

class Waiting(State):
    def __init__(self):
        self.state = 'Waiting' 

class Running(State):
    def __init__(self):
        self.state = 'Running'
        
class New(State):
    def __init__(self):
        self.state = 'New'
    
class Terminated(State):
    def __init__(self):
        self.state = 'Terminated'

class Ready(State):
    def __init__(self):
        self.state = 'Ready'
    

class Memory:
    def __init__(self):
        self.direcciones= []
        
    def get(self, index):
        return self.direcciones[index]
    
    def set(self, index,instruction):
        self.direcciones.insert(index, instruction)
        
    def setLista(self, lista):
        self.direcciones.extend(lista)
        
    def getLastIndex(self):
        return len (self.direcciones)
        
class MemoryManager:
    def __init__(self,memoria):
        self.memoria = memoria
        self.primerCeldaGuardada = 0
        self.ultimaCeldaGuardada = 0
    
    def guardarEnMemoria(self, program):
        self.primerCeldaGuardada = self.ultimaCeldaGuardada
        for instruccion in program.getInstrucciones():
            self.memoria.set(self.ultimaCeldaGuardada,instruccion)
            self.ultimaCeldaGuardada = self.ultimaCeldaGuardada + 1
        
    def getPrimeraCeldaGuardada(self):
        return self.primerCeldaGuardada
    
    def getUltimaCeldaGuardada(self):
        return self.ultimaCeldaGuardada
    
    def get(self, index):
        return self.memoria.get(index)
    
class Kernel:
    def __init__(self,memoria,cola):
        self.memoryManager = MemoryManager(memoria)
        self.pid = 0 ;
        self.cola = cola
    
    def run(self,program,hardDisk,prioridad):
        programa = hardDisk.getPrograma(program)
        self.memoryManager.guardarEnMemoria(programa)
        pcb = Pcb (prioridad, self.memoryManager.getPrimeraCeldaGuardada(), self.pid)
        self.cola.agregarPCB(pcb)
        
        
class Program:
    def __init__(self):
        self.instructions = []

    def addInstruction(self, instruction):
        self.instructions.append(instruction)
        
    def getInstrucciones(self):
        return self.instructions
        
        
class Instruction:
    def __init__ (self, nameInstruction, console):
        self.nombre = nameInstruction
        self.console = console
    def run(self):
        self.console.ingresar('Corriendo: ' + self.nombre + '')
    
class Console:
    def __init__(self):
        self.entradas = []

    def ingresar(self, entrada):
        self.entradas.append(entrada)
        
class Scheduler:
    def __init__(self, cola, politica, quantum):
        self.cola = cola
        self.politica = politica
        self.quantum = quantum
    
    def getQuantum(self):
        return self.quantum
        
        
    def getPcb(self):
        self.politica.getPcb(self.cola)
        
class Politica:
    def __init__(self, cola):
        self.cola = cola 
         
class Fifo(Politica):
# primero en entrar primero en salir
    def getPcb(self):
        return self.cola.popleft()
        
class Prioridad(Politica):
# sale primero el de prioridad menor   
    def getPcb(self):    
        return sorted(self.cola, key=(lambda pcbs: pcbs.prioridad))

class ColaDeReady:
    def __init__(self):
        self.cola = []

    def agregarPCB(self, pcb):
        self.cola.append(pcb)
        
class Pcb:
    def __init__(self, prioridad, posicionBase, pid):
        self.pc = 0
        self.pid = pid
        self.posicionBase = posicionBase
        self.prioridad = prioridad
        
    def getInstruccionEjecutar(self):
        self.pc + self.posicionBase
        
    def incrementarPC(self):
        self.pc = self.pc + 1
        
#    def getPrioridad(self):
 
class Clock(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.listeners = []
    
    def run(self):
        self.timing()

    def register(self, listener):
        self.listeners.append(listener)

    def notify_listeners(self, event):
        for listener in self.listeners:
            listener.tick(event)

    def timing(self):
        while True:
            self.notify_listeners
            print('Clock Tick tock !')
            time.sleep(2)
            #sys.stdout.flush()

class CPU(threading.Thread):    
    def __init__(self, clock, scheduler, quantum, memoryManager):
        threading.Thread.__init__(self)
        self.quantum = quantum
        self.scheduler = scheduler
        self.memoryManager = memoryManager
        clock.register(self)

    def tick(self, event):
        print('CPU Tick tock !')
#        pcb = self.scheduler.getPcb();
#        instruccion = self.memoryManager.get(pcb.getInstruccionEjecutar())
#        pcb.incrementarPC()        

class IrqHandler:
    def __init__(self, ready, memoryManager, scheduler, cpu):
        self.colaReady=ready
        self.memoryManager= memoryManager
        self.scheduler= scheduler
        self.cpu=cpu
        
    def timeOutHandler(self, pcb):
        pcb.status(Ready())
        self.memoryManager.free(pcb)
        pcb.remove(pcb)
        newPcb = self.scheduler.getPcb()
        newPCB.status(Running())
        self.cpu.setPCB(newPcb) 
        
    def killHandler(self, pcb):
        pcb.status(Terminate())
        self.memoryManager.free(pcb)
        pcb.remove(pcb)
        newPcb = self.scheduler.getPcb()
        self.cpu.setPCB(newPcb)     
    
    def ioHandler(self, pcb):
        pcb.status(WaitingIO ())
        newPcb = self.scheduler.getPcb()
        newPCB.status(Ready())
        self.cpu.setPCB(newPcb)         
            
        

clock = Clock()
cpu = CPU(clock)
clock.timing()